﻿using Org.BouncyCastle.Crypto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mmeller.Net.Decoder
{
    public enum CryptoMode
    {
        ECB, CBC, OFB, CFB
    }
    public class CryptoEngine<T> where T : IBlockCipher  
    {

        private Engine<T> cryptor;
        private Engine<T> decryptor;

        private CryptoMode mode;

        public CryptoEngine() : this(CryptoMode.ECB, 0) { }
        public CryptoEngine(CryptoMode mode, int blockLength)
        {
            Type t = typeof(T);
            this.mode = mode;

            this.cryptor = new Engine<T>(mode, blockLength);
            this.decryptor = new Engine<T>(mode, blockLength);
        }

        public void cryptorInit(int sessionKeyLength)
        {
            this.cryptor.init(sessionKeyLength,  this.genereteBytes(sessionKeyLength), this.genereteBytes(16));
        }

        public void decryptorInit(int sessionKeyLength, byte[] sessionKey, byte[] initialVector)
        {
            this.decryptor.init(sessionKeyLength, sessionKey, initialVector);
        }


        private byte[] genereteBytes(int length)
        {
            Random random = new Random();

            byte[] series = new byte[length];
            random.NextBytes(series);
            return series;
        }

        public byte[] encryptData(byte[] inputData)
        {
            return this.cryptor.processData(inputData);
        }

        public byte[] encryptFinalData(int blockSize)
        {
            return this.cryptor.finalProcessData(blockSize);
        }

    }
}

/*
 private PaddedBufferedBlockCipher kryptorNaglowek;
        private PaddedBufferedBlockCipher dekryptorNaglowek;

        private PaddedBufferedBlockCipher kryptorPliku;
        private PaddedBufferedBlockCipher dekryptorPliku;

        public Rc6Szyfrator() : this("ECB", 0) { }

        public Rc6Szyfrator(string tryb, int dlugoscPodbloku)
        {
            this.kryptorNaglowek = new PaddedBufferedBlockCipher(new RC6Engine());
            this.dekryptorNaglowek = new PaddedBufferedBlockCipher(new RC6Engine());

            ustawTryb(tryb, dlugoscPodbloku);
        }

        private void ustawTryb(string tryb, int dlugoscPodbloku)
        {
            switch (tryb)
            {
                case "ECB":
                    this.kryptorPliku = new PaddedBufferedBlockCipher(new RC6Engine());
                    this.dekryptorPliku = new PaddedBufferedBlockCipher(new RC6Engine());
                    break;
                case "CBC":
                    this.kryptorPliku = new PaddedBufferedBlockCipher(new CbcBlockCipher(new RC6Engine()));
                    this.dekryptorPliku = new PaddedBufferedBlockCipher(new CbcBlockCipher(new RC6Engine()));
                    break;
                case "OFB":
                    this.kryptorPliku = new PaddedBufferedBlockCipher(new OfbBlockCipher(new RC6Engine(), dlugoscPodbloku));
                    this.dekryptorPliku = new PaddedBufferedBlockCipher(new OfbBlockCipher(new RC6Engine(), dlugoscPodbloku));
                    break;
                case "CFB":
                    this.kryptorPliku = new PaddedBufferedBlockCipher(new CfbBlockCipher(new RC6Engine(), dlugoscPodbloku));
                    this.dekryptorPliku = new PaddedBufferedBlockCipher(new CfbBlockCipher(new RC6Engine(), dlugoscPodbloku));
                    break;
                default:
                    break;
            }
        }

        public byte[] zaszyfrujElementNaglowka(byte[] input, byte[] klucz)
        {
            this.kryptorNaglowek.Init(true, new KeyParameter(klucz));
            byte[] output = new byte[kryptorNaglowek.GetOutputSize(input.Length)];
            this.kryptorNaglowek.ProcessBytes(input, output, 0);
            this.kryptorNaglowek.DoFinal(output, output.Length - 16);

            return output;
        }

        public byte[] odszyfrujElementNaglowka(byte[] input, byte[] klucz)
        {
            try
            {
                byte[] output = new byte[dekryptorNaglowek.GetOutputSize(input.Length)];
                this.dekryptorNaglowek.Init(false, new KeyParameter(klucz));
                this.dekryptorNaglowek.ProcessBytes(input, output, 0);
                this.dekryptorNaglowek.DoFinal(output, output.Length - 16);

                return output;
            }
            catch (InvalidCipherTextException e)
            {
                throw new ZleHasloException();
            }
        }

        public void szyfrujPlik(string encryptInput, string encryptOutput,
            int dlugoscKlucza, List<String> uzytkownicy, string trybPracy,
            int dlugoscPodbloku,
            ToolStripProgressBar pasekPostepu)
        {
            byte[] kluczSesyjny = wygenerujCiagBajtow(dlugoscKlucza);
            byte[] wektorPoczatkowy = wygenerujCiagBajtow(16);

            HeaderCreator creator = new HeaderCreator();
            creator.utworzNaglowek(encryptOutput, uzytkownicy, kluczSesyjny, dlugoscKlucza, trybPracy, dlugoscPodbloku, wektorPoczatkowy);

            FileStream wejscie = new FileStream(encryptInput, FileMode.Open);
            FileStream wyjscie = new FileStream(encryptOutput, FileMode.Append);

            BinaryReader reader = new BinaryReader(wejscie);
            BinaryWriter writer = new BinaryWriter(wyjscie);

            writer.Seek(0, SeekOrigin.End);
            writer.Write((byte)13);
            writer.Write((byte)10);
            if (trybPracy == "ECB")
            {
                this.kryptorPliku.Init(true, new KeyParameter(kluczSesyjny));
            }
            else
            {
                
                KeyParameter parametr = new KeyParameter(kluczSesyjny);
                ParametersWithIV iv = new ParametersWithIV(parametr, wektorPoczatkowy);
                this.kryptorPliku.Init(true, iv);
            }

            przetwarzaj(reader, writer, this.kryptorPliku, pasekPostepu);

            reader.Close();
            writer.Close();
        }

        public void odszyfrujPlik(string decryptInput, string decryptOutput, string sciezkaDoKlucza, string hasloDoKlucza,
            ToolStripProgressBar pasekPostepu)
        {

            string tempSzyfrogram = @"C:\tempSzyfrogram";
            string tempNaglowek = @"C:\tempNaglowek.xml";

            HeaderCreator creator = new HeaderCreator();

            string nazwaAlgorytmu, trybPracy;
            int dlugoscKlucza, dlugoscPodbloku;
            byte[] kluczSesyjny, wektorPoczatkowy;

            string[] temp = sciezkaDoKlucza.Split('\\');
            string nazwisko = temp[temp.Count() - 2];

            creator.odczytajNaglowek(decryptInput, nazwisko, tempNaglowek, tempSzyfrogram, out nazwaAlgorytmu, out dlugoscKlucza, out kluczSesyjny, out dlugoscPodbloku, out wektorPoczatkowy, out trybPracy, hasloDoKlucza);

            if ((nazwaAlgorytmu == "RC 6") && (kluczSesyjny != null))
            {
                FileStream wejscie = new FileStream(tempSzyfrogram, FileMode.Open);
                FileStream wyjscie = new FileStream(decryptOutput, FileMode.CreateNew);

                BinaryReader reader = new BinaryReader(wejscie);
                BinaryWriter writer = new BinaryWriter(wyjscie);

                if (trybPracy == "ECB")
                {
                    this.dekryptorPliku.Init(false, new KeyParameter(kluczSesyjny));
                }
                else
                {
                    this.ustawTryb(trybPracy, dlugoscPodbloku);
                    KeyParameter parametr = new KeyParameter(kluczSesyjny);
                    ParametersWithIV iv = new ParametersWithIV(parametr, wektorPoczatkowy);
                    this.dekryptorPliku.Init(false, iv);
                }

                przetwarzaj(reader, writer, this.dekryptorPliku, pasekPostepu);

                reader.Close();
                writer.Close();

            }

            File.Delete(tempSzyfrogram);
            File.Delete(tempNaglowek);
        }

        private void przetwarzaj(BinaryReader reader, BinaryWriter writer, PaddedBufferedBlockCipher cipher,
            ToolStripProgressBar pasekPostepu)
        {
            try
            {
                long pozycja = 0;
                long dlugosc = reader.BaseStream.Length;

                int wielkoscBloku = 16 * 12;

                while (pozycja < dlugosc)
                {
                    byte[] niezaszyfrowanyBlokDanych = reader.ReadBytes(wielkoscBloku);
                    byte[] zaszyfrowanyBlokDanych = new byte[kryptorPliku.GetOutputSize(niezaszyfrowanyBlokDanych.Length)];

                    int ilosc2 = cipher.ProcessBytes(niezaszyfrowanyBlokDanych, 0, niezaszyfrowanyBlokDanych.Length, zaszyfrowanyBlokDanych, 0);
                    byte[] temp = new byte[ilosc2];
                    Array.Copy(zaszyfrowanyBlokDanych, temp, ilosc2);
                    writer.Write(temp);
                    pozycja += wielkoscBloku;

                    int wartosc = (int)((pozycja / (float)dlugosc) * 100);
                    pasekPostepu.Value = wartosc;

                }
                byte[] z = new byte[wielkoscBloku];
                int ilosc = cipher.DoFinal(z, 0);
                byte[] tmp = new byte[ilosc];
                Array.Copy(z, tmp, ilosc);
                if (ilosc != 0) writer.Write(tmp);
            }
            catch (InvalidCipherTextException e)
            {
            }

        }

        private byte[] wygenerujCiagBajtow(int dlugosc)
        {
            Random random = new Random();

            byte[] ciagBajtow = new byte[dlugosc];
            random.NextBytes(ciagBajtow);
            return ciagBajtow;
        }
 */

